package com.reger.mybatis.generator;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.api.VerboseProgressCallback;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.InvalidConfigurationException;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import com.alibaba.druid.util.JdbcUtils;
import com.reger.datasource.config.ConfigUtils;
import com.reger.datasource.core.AbstractDataBaseBean;
import com.reger.datasource.core.Mapper;
import com.reger.datasource.properties.DaoProperties;
import com.reger.datasource.properties.DataSourceProperties;
import com.reger.datasource.properties.DruidProperties;
import com.reger.datasource.properties.MybatisNodeProperties;

import io.shardingjdbc.core.constant.DatabaseType;

/**
 * 生成mybatis，model，dao，mapper
 * 
 * @author leige
 *
 */
@SpringBootConfiguration
public class GeneratorMain implements CommandLineRunner, EnvironmentAware {
	@SuppressWarnings("unchecked")
	public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, ClassNotFoundException, NoSuchMethodException   {
		SpringApplication application = new SpringApplication(GeneratorMain.class);
		try {
			application.getClass().getMethod("setWebEnvironment", boolean.class).invoke(application, false);
		} catch (  NoSuchMethodException  e) {
			@SuppressWarnings("rawtypes")
			Class<Enum> pe=(Class<Enum>) Class.forName("org.springframework.boot.WebApplicationType");
			application.getClass().getMethod("setWebApplicationType", pe).invoke(application, Enum.valueOf(pe, "NONE"));
		}
		application.run(args);
	}

	Environment environment;

	@Override
	public void setEnvironment(Environment environment) {
		this.environment =  environment;
	}


	private MybatisNodeProperties getNodeByName(String name) {
		DaoProperties daoProperties = ConfigUtils.getDruidConfig(environment, DaoProperties.dbprefix,
				DaoProperties.class);
		DruidProperties druidProperties = ConfigUtils.getDruidConfig(environment, DruidProperties.druidDefault,
				DruidProperties.class);
		Map<String, MybatisNodeProperties> nodes = daoProperties.getNodes();
		Assert.isTrue(nodes.containsKey(name), "节点" + name + "不存在");
		MybatisNodeProperties node = nodes.get(name);
		Iterator<DataSourceProperties> it = node.getDataSources().values().iterator();
		while (it.hasNext()) {
			DataSourceProperties dataSourceProperties = (DataSourceProperties) it.next();
			DruidProperties master = dataSourceProperties.getMaster();
			if (master == null) {
				master = new DruidProperties();
				dataSourceProperties.setMaster(master);
			}
			master.merge(druidProperties);
		}
		return node;
	}

	@Override
	public void run(String... args) throws Exception {
		Properties properties = PropertiesLoaderUtils.loadProperties(new ClassPathResource("generator.properties"));
		String nodeName = properties.getProperty("mybatis.nodeName", null);
		Assert.hasText(nodeName, "节点名不可以为空");
		MybatisNodeProperties node = this.getNodeByName(nodeName);
		this.buildProperties(properties, node);

		Assert.hasText(properties.getProperty("package.model"), "mapper的model目录不可以为空，配置项 package.model");
		Assert.hasText(properties.getProperty("package.repo"), "mapper的接口目录不可以为空，配置项 package.repo");
		Assert.hasText(properties.getProperty("package.mapper"), "mapper的xml目录不可以为空，配置项 package.mapper");

		this.generate(properties);

	}

	private void buildProperties(Properties properties, MybatisNodeProperties node) throws SQLException {

		DruidProperties master = node.getDataSources().values().iterator().next().getMaster().defaultEmpty();
		String driverClassName = JdbcUtils.getDriverClassName(master.getUrl());
		String dbtype = JdbcUtils.getDbType(master.getUrl(), driverClassName);
		DatabaseType dialect = AbstractDataBaseBean.getDbType(master, master);
	 
		Mapper mapper = Mapper.valueOfDialect(dialect);
		properties.setProperty("jdbc.url", master.getUrl());
		properties.setProperty("jdbc.username", master.getUsername());
		properties.setProperty("jdbc.password", master.getPassword());
		properties.setProperty("jdbc.driverClassName", driverClassName);
		properties.setProperty("jdbc.type", dbtype);
		properties.setProperty("extends.Mapper", mapper.toString());
		if (!StringUtils.hasText(properties.getProperty("extends.modelClass"))) {
			properties.setProperty("extends.modelClass", Object.class.getName());
		}
		if (!StringUtils.hasText(properties.getProperty("java.delimiter"))) {
			properties.setProperty("java.delimiter", "");
		}
		if (!StringUtils.hasText(properties.getProperty("java.encoding"))) {
			properties.setProperty("java.encoding", "UTF-8");
		}
		if (!StringUtils.hasText(properties.getProperty("package.model"))) {
			properties.setProperty("package.model", this.getFirstPackage(node.getTypeAliasesPackage()));
		}
		if (!StringUtils.hasText(properties.getProperty("package.repo"))) {
			properties.setProperty("package.repo", this.getFirstPackage(node.getBasePackage()));
		}
		if (!StringUtils.hasText(properties.getProperty("package.mapper"))) {
			String alias=this.getFirstPackage(node.getMapperPackage());
			if(StringUtils.hasText(alias)){
				alias=ClassUtils.convertClassNameToResourcePath(alias);
			}else{
				alias="";
			}
			properties.setProperty("package.mapper",alias);
		}
	}

	private String getFirstPackage(String packageStrs) {
		if (!StringUtils.hasText(packageStrs))
			return "";
		String[] packages = packageStrs.split(",");
		for (String packageStr : packages) {
			packageStr = packageStr.trim();
			if (packageStr.indexOf("*") == -1) {
				return packageStr;
			}
		}
		return "";
	}

	private void generate(Properties properties)throws IOException, XMLParserException, SQLException, InterruptedException, InvalidConfigurationException {
		ClassPathResource resource = new ClassPathResource("META-INF/generator/generatorconfig.xml");
		List<String> warnings = new ArrayList<String>();
		ConfigurationParser cp = new ConfigurationParser(properties, warnings);
		Configuration config = cp.parseConfiguration(resource.getInputStream());
		System.err.println("#############################################################################################");
		System.err.println("######################################开始生成##################################################");
		System.err.println("#############################################################################################");
		new MyBatisGenerator(config, new DefaultShellCallback(true), warnings).generate(new VerboseProgressCallback());
		System.err.println("#############################################################################################");
		System.err.println("#######################################生成完毕#################################################");
		System.err.println("#############################################################################################");
	}

}
